package applog

import (
	"errors"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/golang/factory"
	"gitee.com/ymofen/golog"
	"gitee.com/ymofen/lumberjack"
	"gitee.com/ymofen/supervalue"
	"log/slog"
	"os"
	"strconv"
	"strings"
	"sync"
)

// 设定slog的输出, 只能有一个
type PlgLogConf struct {
	PlugId     string
	closeFuncs *gobase.SyncFuncList
	lk         sync.RWMutex
	fileName   string
	zip        bool
	roteSize   int
	filew      *lumberjack.Logger
	multiW     *gobase.MultiWriter
	defLogger  *slog.Logger
	defGoLog   *golog.GoLog

	//goLogOption golog.GoLogOption
	confRaw string
}

func (this PlgLogConf) OnCreate() {

}

func (this *PlgLogConf) Close() error {
	this.lk.Lock()
	slog.SetDefault(this.defLogger)
	golog.SetDefaultLogger(this.defGoLog)
	this.closePreFileW()
	this.lk.Unlock()
	return nil
}

func (this *PlgLogConf) closePreFileW() {
	if this.filew != nil {
		this.filew.Close()
		this.filew = nil
	}
}

func ParseGoLogLevel(s string) (l golog.GoLogLevel, err error) {
	defer func() {
		if err != nil {
			err = fmt.Errorf("golog: level string %q: %w", s, err)
		}
	}()

	name := s
	offset := 0
	if i := strings.IndexAny(s, "+-"); i >= 0 {
		name = s[:i]
		offset, err = strconv.Atoi(s[i:])
		if err != nil {
			return golog.LevelDebug, err
		}
	}
	switch strings.ToUpper(name) {
	case "DEBUG":
		l = golog.LevelDebug
	case "INFO":
		l = golog.LevelInfo
	case "WARN":
		l = golog.LevelWarn
	case "ERROR":
		l = golog.LevelError
	default:
		return golog.LevelDebug, errors.New("unknown name")
	}
	l += golog.GoLogLevel(offset)
	return
}

func (this *PlgLogConf) checkNewLogger(conf *wrapper.SuperValue) {
	fileName := conf.StringByName("fileName", "")
	if len(fileName) == 0 {
		slog.SetDefault(this.defLogger)
		golog.SetDefaultLogger(this.defGoLog)
		this.closePreFileW()
		return
	}

	fileW := &lumberjack.Logger{
		Filename:   fileName,
		LocalTime:  conf.BoolByName("localTime", true),
		MaxSize:    conf.IntByName("maxSize", 50),
		MaxAge:     conf.IntByName("maxAge", 7),
		MaxBackups: conf.IntByName("maxBackups", 7),
		Compress:   conf.BoolByName("compress", false),
	}

	multiW := gobase.NewMultiLogW()
	multiW.AddWriter(fileW)

	if conf.BoolByName("console", true) {
		multiW.AddWriter(os.Stdout)
	}
	{
		var opts []golog.GoLogOptionFunc
		lvlstr := conf.StringByName("LogLevel", "")
		if len(lvlstr) > 0 {
			lvl, _ := ParseGoLogLevel(lvlstr)
			opts = append(opts, golog.WithOptionLevel(lvl))
		}

		str := conf.StringByName("addSource", "")
		if len(str) > 0 {
			opts = append(opts, golog.WithOptionAddSource(gobase.TryStrToBool(str, false)))
		}

		golog.SetDefaultLogger(golog.Default().WithOptionEx(opts...))
		log1 := golog.Default().WithHandler(golog.WithFormatAndWriterHandler(multiW, "")).WithOptionEx(opts...)
		golog.SetDefaultLogger(log1)
	}

	logLvl, _ := ParseSlogLevel(conf.StringByName("logLevel", "INFO"))
	opts := &slog.HandlerOptions{
		AddSource: golog.Default().GetOption().AddSource,
		Level:     logLvl,
	}

	logger := slog.New(NewKplTextHandler(multiW, opts))
	slog.SetDefault(logger)

	this.closePreFileW()
	this.multiW = multiW
	this.filew = fileW
	this.fileName = fileName
}

func (this *PlgLogConf) ReloadConf(conf *wrapper.SuperValue) error {

	confstr := conf.String()

	this.lk.Lock()
	defer this.lk.Unlock()

	if this.defLogger == nil {
		this.defLogger = slog.Default()
	}

	if this.defGoLog == nil {
		this.defGoLog = golog.DefaultLogger()
	}

	if confstr != this.confRaw {
		this.checkNewLogger(conf)
		this.confRaw = confstr
	}

	return nil
}

func CreatePlgLogConfFunc(args ...interface{}) (rval interface{}, err error) {
	obj := &PlgLogConf{}

	if len(args) > 0 {
		if str, ok := args[0].(string); ok {
			obj.PlugId = str
		}
	}
	rval = obj
	return
}

func init() {
	factory.DefaultCreator.RegisterCreator("plgs.logconf", CreatePlgLogConfFunc)
}
